//
// Created by wangwanlong on 2024/11/1.
//

#ifndef MKAHYPAR_VERTEX_PAIR_RATER_H
#define MKAHYPAR_VERTEX_PAIR_RATER_H

#include "mkahypar/partition/coarsening/policies/rating_score_policy.h"
#include "mkahypar/partition/coarsening/policies/rating_heavy_node_penalty_policy.h"
#include "mkahypar/partition/coarsening/policies/rating_community_policy.h"
#include "mkahypar/partition/coarsening/policies/rating_partition_policy.h"
#include "mkahypar/partition/coarsening/policies/rating_acceptance_policy.h"
#include "mkahypar/partition/context.h"
#include "mkahypar/datastructure/sparse_map.h"
//缩合打分函数
namespace mkahypar {
  template<class ScorePolicy = HeavyEdgeScore,
          class HeavyNodePenaltyPolicy = MultiplicativePenalty,
          class CommunityPolicy = UseCommunityStructure,
          class RatingPartitionPolicy = NormalPartitionPolicy,
          class AcceptancePolicy = BestRatingWithTieBreaking<>,
          typename RatingType = RatingType>
  class VertexPairRater {
  private:
    class VertexPairRating {
    public:
      VertexPairRating(HypernodeID trgt, RatingType val, bool is_valid) :
              target(trgt),
              value(val),
              valid(is_valid) {}

      VertexPairRating() :
              target(std::numeric_limits<HypernodeID>::max()),
              value(std::numeric_limits<RatingType>::min()),
              valid(false) {}

      VertexPairRating(const VertexPairRating &) = delete;

      VertexPairRating &operator=(const VertexPairRating &) = delete;

      VertexPairRating(VertexPairRating &&) = default;

      VertexPairRating &operator=(VertexPairRating &&) = delete;

      ~VertexPairRating() = default;

      HypernodeID target;
      RatingType value;
      bool valid;
    };

  public:
    using Rating = VertexPairRating;

    VertexPairRater(Hypergraph &hypergraph) :
            _hg(hypergraph),
            _tmp_ratings(_hg.initialNumNodes()),
            _already_matched(_hg.initialNumNodes()) {}

    VertexPairRater(const VertexPairRater &) = delete;

    VertexPairRater &operator=(const VertexPairRater &) = delete;

    VertexPairRater(VertexPairRater &&) = delete;

    VertexPairRater &operator=(VertexPairRater &&) = delete;

    ~VertexPairRater() = default;

    //计算公式参加
    VertexPairRating rate(const HypernodeID u) {
      NodeWeight& weight_u = _hg.nodeWeight(u);
      for (const HyperedgeID &he: _hg.incidentEdges(u)) {
        //大边过滤，提升允许效率
        if (_hg.edgeSize(he) <= Context::getInstance().hyperedge_size_threshold) {
          //超边打分函数：边权/(size-1)
          const RatingType score = ScorePolicy::score(_hg, he);
          for (const HypernodeID &v: _hg.pins(he)) {

            if (v != u && belowThresholdNodeWeight(weight_u, _hg.nodeWeight(v)) &&
                RatingPartitionPolicy::accept(_hg, u, v)) {
              //优先选择同分区且顶点权重更大的
              _tmp_ratings[v] += score;
            }

          }
        }
      }

      RatingType max_rating = std::numeric_limits<RatingType>::min();
      HypernodeID target = std::numeric_limits<HypernodeID>::max();
      for (auto it = _tmp_ratings.end() - 1; it >= _tmp_ratings.begin(); --it) {
        const HypernodeID tmp_target = it->key;
        NodeWeight & target_weight = _hg.nodeWeight(tmp_target);
        //计算权重惩罚，越大说明，这个值应该不会为0
        RatingType penalty = calNodeScore(weight_u,target_weight);

        const RatingType tmp_rating = it->value / penalty;
        if (CommunityPolicy::sameCommunity(_hg.communities(), u, tmp_target) &&AcceptancePolicy::acceptRating(tmp_rating, max_rating,
                                           target, tmp_target, _already_matched)) {
          max_rating = tmp_rating;
          target = tmp_target;
        }
      }

      VertexPairRating ret;
      if (max_rating != std::numeric_limits<RatingType>::min()) {
        ret.value = max_rating;
        ret.target = target;
        ret.valid = true;
      }
      _tmp_ratings.clear();
      return ret;
    }

    void markAsMatched(const HypernodeID hn) {
      _already_matched.set(hn, true);
    }

    void resetMatches() {
      _already_matched.reset();
    }

    //节点缩合上界
    NodeWeight &thresholdNodeWeight() const {
      return Context::getInstance().max_allowed_node_weight;
    }

    //节点对打分(多维)
    RatingType calNodeScore(NodeWeight &weight_u, NodeWeight &weight_v) {
      RatingType part_score = 8;
      RatingType score = 0;
      for (int i = 0; i < weight_u.size(); ++i) {
        if(thresholdNodeWeight()[i] == 0)
          continue;
        score += part_score * ((weight_u[i] + weight_v[i]) / thresholdNodeWeight()[i]);
      }
      return score;
    }

  private:
    //确保不会出现超大的顶点
    bool belowThresholdNodeWeight(const NodeWeight &weight_u,
                                  const NodeWeight &weight_v) const {


      return weight_v + weight_u <= Context::getInstance().max_allowed_node_weight;
    }

    Hypergraph &_hg;
    ds::SparseMap<HypernodeID, RatingType> _tmp_ratings;
    ds::FastResetFlagArray<> _already_matched;
  };
}
#endif //MKAHYPAR_VERTEX_PAIR_RATER_H
